package midoo.server.core;

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.internal.ConcurrentSet;
import midoo.common.MConfig;
import midoo.common.MService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

/**
 * Created by ajianzheng on 2015/4/10.
 *
 */
public class MChannelStatistics implements MService {
    protected static final Logger LOGGER = LoggerFactory.getLogger("STAT");

    private static MChannelStatistics ourInstance = new MChannelStatistics();

    public static MChannelStatistics getInstance() {
        return ourInstance;
    }

    private MChannelStatistics() {
    }

    ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();

    @Override
    public int start(MConfig cfg) throws Exception {
        MChannelStatisticsConfig config = (MChannelStatisticsConfig)cfg;

        service.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                dumpAll();

                updateStat();
            }
        }, config.getDelay(), config.getDelay(), TimeUnit.SECONDS);

        return 0;
    }

    @Override
    public void stop() {
        service.shutdown();
    }

    ///
    private long startTimeMillis = System.currentTimeMillis();      /// 系统启动时间

//    private AtomicInteger totalChannels = new AtomicInteger(0);     /// 连接客户端数量
    private Set<Channel> totalChannels = new ConcurrentSet<>();

    private AtomicLong totalRequests = new AtomicLong(0);           /// 总请求数
    private AtomicLong totalSuccessRequests = new AtomicLong(0);    /// 成功请求数

    private long lastRequests = 0;
    private long lastSuccessRequests = 0;

    /// 流量统计
    private AtomicLong totalReadBytes = new AtomicLong(0);
    private AtomicLong totalWriteBytes = new AtomicLong(0);
    private long lastReadBytes = 0;
    private long lastWriteBytes = 0;

    /// 时延
    private int maxDelayValue = 0;
    private int mimDelayValue = 0;
    private long totalDelayValue = 0;

    private void updateStat() {
        lastRequests = totalRequests.get();
        lastSuccessRequests = totalSuccessRequests.get();

        lastReadBytes = totalReadBytes.get();
        lastWriteBytes = totalWriteBytes.get();
    }

    public int addChannel(ChannelHandlerContext ctx) {
        totalChannels.add(ctx.channel());

        return totalChannels.size();
    }

    public int removeChannel(ChannelHandlerContext ctx) {
        totalChannels.remove(ctx.channel());

        return totalChannels.size();
    }

    public long newRequest() {
        return totalRequests.addAndGet(1L);
    }

    public long newBytesRead(int bytes) {
        return totalReadBytes.addAndGet(bytes);
    }

    public long newBytesWritten(int bytes) {
        return totalWriteBytes.addAndGet(bytes);
    }

    static SimpleDateFormat DATA_FORMAT = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");

    public void dumpAll() {
        LOGGER.info("\r\n");
        LOGGER.info("************************************************************");
        LOGGER.info("* Now Time: {}", DATA_FORMAT.format(new Date()));
        LOGGER.info("* System uptime    : {}", millsSecondsToHms(System.currentTimeMillis() - startTimeMillis));

        LOGGER.info("* Total clients    : {}", totalChannels.size());
        LOGGER.info("* Total request    : {}", totalRequests);
//        LOGGER.info("* Success rate     : {}", totalSuccessRequests.get()/totalRequests.get());
        LOGGER.info("* Current request  : {}", totalRequests.get() - lastRequests);
        LOGGER.info("* Total mimDelay   : {}", mimDelayValue);
        LOGGER.info("* -----------------------------");
        LOGGER.info("* Client IP         Online time");
        LOGGER.info("************************************************************");
    }

    static String millsSecondsToHms(long millsSeconds) {
        String result = "";

        millsSeconds/=1000;

        long i = millsSeconds/3600;
        if (i > 0) result += i+"h";

        millsSeconds %= 3600;

        i = millsSeconds/60;
        if (!result.isEmpty() || i>0) result += i+"m";

        result += millsSeconds%60 +"s";

        return result;
    }
}
